package im.oen.boot.common.utils;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * Json工具
 *
 * @author Kevin.Xu
 */
public class JsonUtil {

    private static ObjectMapper MAPPER;

    static {
        MAPPER = new ObjectMapper();
        MAPPER.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        MAPPER.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        MAPPER.configure(SerializationFeature.FAIL_ON_UNWRAPPED_TYPE_IDENTIFIERS, false);
        MAPPER.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        MAPPER.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
            @Override
            public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
                jsonGenerator.writeString("");
            }
        });
        MAPPER.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
        MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
    }

    public static String toJson(final Object obj) throws JsonProcessingException {
        if (Checker.isEmpty(obj)) {
            return null;
        }
        return MAPPER.writeValueAsString(obj);
    }

    public static String safeToJson(final Object obj) {
        if (Checker.isEmpty(obj)) {
            return null;
        }
        return Runner.safeRun(() -> {
            return MAPPER.writeValueAsString(obj);
        });
    }

    public static <T> T toObj(final String json, final Class<T> clazz) throws JsonProcessingException {
        if (Checker.isEmpty(json)) {
            return null;
        }
        return MAPPER.readValue(json, clazz);
    }

    public static <T> T safeToObj(final String json, final Class<T> clazz) {
        if (Checker.isEmpty(json)) {
            return null;
        }

        return Runner.safeRun(() -> {
            return MAPPER.readValue(json, clazz);
        });
    }

    public static <T, S> T safeToObj(final String json, final Class<T> clazz, final Class<S> type) {
        if (Checker.isEmpty(json)) {
            return null;
        }

        return Runner.safeRun(() -> {
            JavaType javaType = MAPPER.getTypeFactory().constructParametricType(clazz, type);
            return MAPPER.readValue(json, javaType);
        });
    }

    public static <T> List<T> safeToList(final String json, final Class<T> clazz) {
        if (Checker.isEmpty(json)) {
            return null;
        }

        return Runner.safeRun(() -> {
            JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, clazz);
            return MAPPER.readValue(json, javaType);
        });
    }

    public static <T> Set<T> safeToSet(final String json, final Class<T> clazz) {
        if (Checker.isEmpty(json)) {
            return null;
        }

        return Runner.safeRun(() -> {
            JavaType javaType = MAPPER.getTypeFactory().constructParametricType(Set.class, clazz);
            return MAPPER.readValue(json, javaType);
        });
    }

    public static <K, V> Map<K, V> safeToMap(final String json, final Class<K> classKey, final Class<V> classValue) {
        if (Checker.isEmpty(json)) {
            return null;
        }

        return Runner.safeRun(() -> {
            JavaType javaType = MAPPER.getTypeFactory().constructParametricType(HashMap.class, classKey, classValue);
            return MAPPER.readValue(json, javaType);
        });
    }

    public static JsonNode toObj(final String json) throws JsonProcessingException {
        if (Checker.isEmpty(json)) {
            return null;
        }
        return MAPPER.readTree(json);
    }

    public static JsonNode safeToObj(final String json) {
        if (Checker.isEmpty(json)) {
            return null;
        }
        return Runner.safeRun(() -> {
            return MAPPER.readTree(json);
        });
    }

    public static <T> T getValue(JsonNode node, String name) {
        if (Checker.isEmpty(node)) {
            return null;
        }
        JsonNode n = node.get(name);
        if (Checker.isEmpty(n)) {
            return null;
        }
        return (T) n.asText();
    }

}
